﻿namespace EyeOpen.IO.Ports
{
	using System;
	using System.Collections.Generic;
	using System.IO.Ports;
	using System.Linq;

	public class PhysicalSerialPort 
		: IPort
	{
		private readonly ILogger logger;
		private readonly SerialPort serialPort;

		public PhysicalSerialPort(ChannelConfiguration channelConfiguration)
			: this(channelConfiguration, new NullLogger())
		{
		}

		public PhysicalSerialPort(ChannelConfiguration channelConfiguration, ILogger logger)
		{
			this.logger = logger;
			serialPort = CreateSerialPort(channelConfiguration);

			serialPort.DataReceived += SerialPortDataReceived;
		}

		public event EventHandler<SerialDataReceivedEventArgs> DataReceived;

		public int BytesToRead
		{
			get { return serialPort.BytesToRead; }
		}

		public bool IsOpen
		{
			get { return serialPort.IsOpen; }
		}

		public bool DtrEnable
		{
			get { return serialPort.DtrEnable; }
			set { serialPort.DtrEnable = value; }
		}

		public bool RtsEnable
		{
			get { return serialPort.RtsEnable; }
			set { serialPort.RtsEnable = value; }
		}

		public void DiscardOutBuffer()
		{
			serialPort.DiscardOutBuffer();
		}

		public void DiscardInBuffer()
		{
			serialPort.DiscardInBuffer();
		}

		public void Read(byte[] bytes, int i, int numOfBytes)
		{
			serialPort.Read(bytes, i, numOfBytes);
		}

		public void Write(byte[] arrayByte, int i, int length)
		{
			serialPort.Write(arrayByte, i, length);
		}

		public void Open()
		{
			serialPort.Open();
		}

		public void Close()
		{
			serialPort.Close();
		}

		public void Dispose()
		{
			serialPort.Dispose();
		}

		protected virtual void OnDataReceived(SerialDataReceivedEventArgs e)
		{
			var handler = DataReceived;

			if (handler == null)
			{
				return;
			}

			handler(this, e);
		}

		protected string ConvertArrayByteToString(IList<byte> byteArrayList, bool separaByteConSpazio)
		{
			var a = string.Empty;

			for (var i = 0; i < byteArrayList.Count; i++)
			{
				if (separaByteConSpazio)
				{
					a += byteArrayList[i].ToString("X2") + " ";
				}
				else
				{
					a += byteArrayList[i].ToString("X2");
				}
			}

			return a;
		}

		private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
		{
			var numOfBytes = serialPort.BytesToRead;
			var bytes = new byte[numOfBytes];
			serialPort.Read(bytes, 0, numOfBytes);
			logger.WriteLine("[{0}] - {1}", numOfBytes, ConvertArrayByteToString(bytes.ToList(), false));

			this.OnDataReceived(e);
		}

		private SerialPort CreateSerialPort(ChannelConfiguration channelConfiguration)
		{
			return
				new SerialPort(
					channelConfiguration.PortName,
					channelConfiguration.BaudRate,
					channelConfiguration.Parity,
					channelConfiguration.DataBits,
					channelConfiguration.StopBits);
		}
	}
}
